package com.srp.servise.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.CSVWriter;
import com.opencsv.exceptions.CsvException;
import com.srp.common.R;
import com.srp.dto.DataFileDto;
import com.srp.dto.TreeDto;
import com.srp.mapper.TestDataMapper;
import com.srp.mapper.TreeMapper;
import com.srp.mapper.UserTreeMapper;
import com.srp.pojo.DataFile;
import com.srp.pojo.DecisionTreeNode;
import com.srp.pojo.TestDataFile;
import com.srp.servise.TreeService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.srp.tools.FederalLearningModel;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

@Service
@Slf4j
public class TreeServiceImpl extends ServiceImpl<TreeMapper, DataFile> implements TreeService {
@Autowired
FederalLearningModel federalLearningModel;
@Autowired
TreeMapper treeMapper;
@Autowired
UserTreeMapper userTreeMapper;
@Autowired
TestDataMapper testDataMapper;
@Value("${files.upload.path}")
private String basePath;



    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean generateTree(String treeId,String code, String name,String userId) throws IOException, InterruptedException {
        try {
            int A =userTreeMapper.getCountByIdAndTreeId(userId,treeId);
            if(userTreeMapper.getCountByIdAndTreeId(userId,treeId)>=1){
                return false;
            }


            String[] words = code.split("\\s+");

            // 使用lastIndexOf和substring方法提取文件名
            String url = treeMapper.getUrlWithName(name);
            int lastSlashIndex = url.lastIndexOf('/');
            String filename = url.substring(lastSlashIndex + 1);

            String[] dataArg = new String[]{"-datasetPath", basePath + filename};
            // 创建新数组，长度为原始数组长度加上新元素数量
            int newLength = words.length + dataArg.length;
            String[] extendedArray = new String[newLength];

            // 复制原始数组的元素到新数组
            System.arraycopy(words, 0, extendedArray, 0, words.length);

            // 复制新元素到新数组
            System.arraycopy(dataArg, 0, extendedArray, words.length, dataArg.length);


            String decision_tree_structure = federalLearningModel.id3gini(extendedArray);
            treeMapper.insertIdAndDecisionTreeStructure(treeId, decision_tree_structure,name);
            userTreeMapper.insert(userId,treeId);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }

//        try{
//            federalLearningModel.id3gini(code,"-datasetPath",treeMapper.getFilePathWithName(name));
//            return true;
//        } catch (IOException e) {
//            e.printStackTrace();
//            return false;
//        }


        return false;
    }

    @Override
    public List<String> getRight(String user_id) {
        List<String> result = treeMapper.getDeleteRight(user_id);
        return result;
    }

    @Override
    public void giveUserFileRight(String user_id, int file_id) {
        treeMapper.giveUserFileRight(user_id,file_id);
    }

    @Override
    public void deleteRowsByFileId(String file_id){
        treeMapper.deleteRowsByFileId(file_id);
    }

    @Override
    public R<Boolean> uploadTestData(MultipartFile file) {
        List<String> testDataNames = treeMapper.getTestDataName();
        String originalFilename = file.getOriginalFilename();
        String baseFileName = originalFilename.substring(0, originalFilename.lastIndexOf('.'));
        String suffix = originalFilename.substring(originalFilename.lastIndexOf('.'));

        String newFileName = generateUniqueFileName(baseFileName, suffix, testDataNames);

        // 使用uuid重新生成文件名，防止文件名称重复造成文件覆盖
        String fileName = UUID.randomUUID().toString()+suffix;
        // 创建目录对象
        File dir = new File(basePath);
        if(!dir.exists()){
            dir.mkdirs();
        }
        try{
            file.transferTo(new File(basePath+fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }

        treeMapper.insertTestData(newFileName,fileName);
        return R.success(true);
    }

    @Override
    public R<String> getFeedback(String treeId, int dataId, HttpServletResponse response) {
        // 根据treeId获取决策树结构
        String decisionTree = treeMapper.getDecisionTreeById(treeId);
        // 解析决策树
        DecisionTreeNode root = parse(decisionTree);
        printTree(root,0);
        // 根据dataId找到file，生成结果
        String url = treeMapper.findTestDataUrl(dataId);
        String excelPath = basePath+url;
        try (CSVReader csvReader = new CSVReaderBuilder(new FileReader(excelPath)).build();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
             CSVWriter csvWriter = new CSVWriter(new OutputStreamWriter(outputStream))) {
            String[] featureNames = csvReader.readNext();  // 读取特征行
            List<String[]> records = csvReader.readAll();

            String[] extendedFeatureNames = new String[featureNames.length + 1];
            System.arraycopy(featureNames, 0, extendedFeatureNames, 0, featureNames.length);
            extendedFeatureNames[featureNames.length] = "result";

            csvWriter.writeNext(extendedFeatureNames);

            for (String[] record : records) {
                String predictedLabel = classify(root, record, featureNames);

                String[] newRecord = new String[record.length + 1];
                System.arraycopy(record, 0, newRecord, 0, record.length);
                newRecord[record.length] = predictedLabel;

                csvWriter.writeNext(newRecord);
            }

            csvWriter.flush();
            byte[] csvData = outputStream.toByteArray();

            response.setContentType("text/csv");
            response.setHeader("Content-Disposition", "attachment; filename=output.csv");

            OutputStream out = response.getOutputStream();
            out.write(csvData);

            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
            return R.error("分析结果失败");
        } catch (CsvException e) {
            return R.error("分析结果失败");
        }

        return R.success("分析结果成功");
    }

    @Override
    public R<Boolean> deleteTestData(List<String> ids) {
        for(String id:ids){
           treeMapper.deleteTestDataById(id);
        }
        return R.success(true);
    }

    @Override
    public R<List<DataFileDto>> getTestDataList() {
        List<TestDataFile> dataFiles = testDataMapper.selectAll();
        List<DataFileDto> dataFileDtos = new ArrayList<>();
        for(TestDataFile dataFile: dataFiles){
            DataFileDto dto = new DataFileDto();
            dto.setId(dataFile.getId());
            dto.setName(dataFile.getName());
            dataFileDtos.add(dto);
        }
        return R.success(dataFileDtos);
    }

    @Override
    public R<String> downloadTestData(String id,HttpServletResponse response) throws IOException {
        // 根据id获取文件路径
        String url = testDataMapper.getUrlById(id);
        String filePath = basePath+url;
        File file = new File(filePath);
        log.info(file.getPath());

        response.setCharacterEncoding("UTF-8");
        response.addHeader("Content-Length", "" + file.length());
        response.setHeader("Content-Disposition", "attachment;filename=testData.csv");
        response.setContentType("application/octet-stream");

        InputStream input = null;
        OutputStream output = null;
        try{
            input = new FileInputStream(file); //创建文件输入流对象
            output = response.getOutputStream(); //创建文件输出流对象
            byte[] buf = new byte[1024]; //定义一个字节数组作为缓冲区，并指定一次读取1024个字节
            int len; //记住读入缓冲区的字节长度
            while ((len = input.read(buf)) != -1){ //判断文件是否读完（读完后会返回-1）
                output.write(buf, 0, len); //从buf缓冲区中读取数据，从第一个字节开始读，一次读取len，并将读取出来的数据写入文件输出流对象中
            }
            input.close();
            output.close();
            return R.success("文件下载成功");
        }catch (Exception e){
            e.printStackTrace();
            input.close();
            output.close();
            return R.error("文件下载失败");
        }

    }

    @Override
    public R<List<DataFileDto>> getDataList(String userId) {
        List<String> fileIds = treeMapper.getDeleteRight(userId);
        List<DataFileDto> dataFileDtos = new ArrayList<>();
        for(String fileId:fileIds){
            DataFileDto dto = new DataFileDto();
            DataFile dataFile = getById(fileId);
            dto.setId(dataFile.getId());
            dto.setName(dataFile.getName());
            dataFileDtos.add(dto);
        }
        return R.success(dataFileDtos);
    }

    @Override
    public R<Map<String, List<String>>> getAttributes(String treeId) {
        // 根据treeId获取决策树结构
        String decisionTree = treeMapper.getDecisionTreeById(treeId);
        // 解析决策树
        DecisionTreeNode root = parse(decisionTree);

        HashMap<String,List<String>> map = new HashMap<>();

        map = collectAttributesAndValues(root,map);
        return R.success(map);
    }

    @Override
    public R<String> getFeedback(Map<String, String> data,String treeId) {
        // 根据treeId获取决策树结构
        String decisionTree = treeMapper.getDecisionTreeById(treeId);
        // 解析决策树
        DecisionTreeNode root = parse(decisionTree);
        String[] featureNames = new String[data.size()];
        String[] record = new String[data.size()];
        int index = 0;
        for (Map.Entry<String, String> entry : data.entrySet()) {
            featureNames[index] = entry.getKey();
            record[index] = entry.getValue();
            index++;
        }

        String ans = classify(root,record,featureNames);
        return R.success(ans);
    }



    private static String classify(DecisionTreeNode node, String[] data, String[] featureNames) {
        if (node == null) {
            return "Unknown";
        }

        if (node.branches == null) {
            return node.result;
        }

        for (DecisionTreeNode branch : node.branches) {
            int index = getIndex(branch.attribute, featureNames);
            if (index >= 0 && branch.value.equals(data[index]+":")) {
                return classify(branch, data, featureNames);
            }
        }

        return "Unknown";
    }

    private static int getIndex(String attribute, String[] featureNames) {
        for (int i = 0; i < featureNames.length; i++) {
            if (featureNames[i].equals(attribute)) {
                return i;
            }
        }
        return -1;
    }

    private static String generateUniqueFileName(String baseFileName, String suffix, List<String> existingFilenames) {
        String newFileName = baseFileName + suffix;

        if (existingFilenames.contains(newFileName)) {
            int index = 1;

            // Find the highest index in parentheses
            for (String existingFilename : existingFilenames) {
                if (existingFilename.startsWith(baseFileName + "(") && existingFilename.endsWith(")" + suffix)) {
                    int endIndex = existingFilename.indexOf(')');
                    int startIndex = existingFilename.lastIndexOf('(', endIndex - 1) + 1;

                    String indexStr = existingFilename.substring(startIndex, endIndex);
                    int currentIndex = Integer.parseInt(indexStr);

                    index = Math.max(index, currentIndex + 1);
                }
            }

            newFileName = baseFileName + "(" + index + ")" + suffix;
        }

        return newFileName;
    }

    @Override
    public R<Object> statisticAnalysis(String treeName, int dataId){
        // 根据treeId获取决策树结构
        String decisionTree = treeMapper.getDecisionTreeById(treeName);
        // 解析决策树
        DecisionTreeNode root = parse(decisionTree);
        printTree(root,0);
        // 根据dataId找到file，生成结果
        String url = treeMapper.findTestDataUrl(dataId);
        String excelPath = basePath+url;
        try (CSVReader csvReader = new CSVReaderBuilder(new FileReader(excelPath)).build();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
             CSVWriter csvWriter = new CSVWriter(new OutputStreamWriter(outputStream))) {
            String[] featureNames = csvReader.readNext();  // 读取特征行
            List<String[]> records = csvReader.readAll();

            String[] extendedFeatureNames = new String[featureNames.length + 1];
            System.arraycopy(featureNames, 0, extendedFeatureNames, 0, featureNames.length);
            extendedFeatureNames[featureNames.length] = "result";

            csvWriter.writeNext(extendedFeatureNames);

            //统计数据
            Map<String, Integer> countMaps = new HashMap<>();
            for (String[] record : records) {
                String predictedLabel = classify(root, record, featureNames);


                if(countMaps.containsKey(predictedLabel)){
                    countMaps.put(predictedLabel, countMaps.get(predictedLabel) + 1);
                }else{
                    countMaps.put(predictedLabel, 1);
                }

            }
        return R.success(countMaps);
        }
        catch (IOException e) {
            e.printStackTrace();
            return R.error("失败");
        } catch (CsvException e) {
            return R.error("失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteTree(List<String> treeNames, String userId) throws IOException, InterruptedException {
        try{
            for (String treeName:
                    treeNames) {
                if (userTreeMapper.selectByIdAndTreeId(userId,treeName)!=null)
                {userTreeMapper.delById(userId,treeName);
                    treeMapper.delById(treeName);
                    }
                else{
                    return false;}
            }
            return true;

        } catch (Exception e) {
            e.printStackTrace();

        }
        return false;
    }

    public List<TreeDto> getTreeList(String userId) {
       List<String> treeNames = userTreeMapper.selectById(userId);
        List<TreeDto> TreeDtos = new ArrayList<>();
        for (String treeName:
        treeNames) {
            TreeDto temp = new TreeDto(treeName,treeMapper.getFileName(treeName))  ;
            TreeDtos.add(temp);
        }
    return TreeDtos;
    }

    @Override
    public String getTreeDetail(String id, String userId) {
        try{
            if (userTreeMapper.selectByIdAndTreeId(userId,id)!=null)
            {
                String decisionTree = treeMapper.getDecisionTreeById(id);
                return decisionTree;}
            else{
                return null;}

        } catch (Exception e) {
            e.printStackTrace();

        }
        return null;
    }

    public static DecisionTreeNode parse(String input) {
        Stack<DecisionTreeNode> stack = new Stack<>();
        DecisionTreeNode root = new DecisionTreeNode(null, null, new ArrayList<>(), null);

        String[] lines = input.split("\n");
        lines = Arrays.copyOfRange(lines, 1, lines.length);
        for (String line : lines) {
            int depth = getIndentLevel(line);
            line = line.replaceAll("^(\\s*\\|)+", "").trim();
            String[] parts = line.trim().split(" ");

            String attribute = parts[1];  //safety
            String value = parts[3];  // high

            if (parts.length == 5) {
                // Leaf node
                String result = parts[4];  //acc
                DecisionTreeNode leaf = new DecisionTreeNode(attribute, value, null, result);

                while (stack.size()-1>=depth){
                    stack.pop();
                }

                if(!stack.isEmpty()){
                    DecisionTreeNode parent = stack.peek();
                    if (parent.branches == null) {
                        parent.branches = new ArrayList<>();
                    }
                    parent.branches.add(leaf);
                }else{
                    root.branches.add(leaf);
                }


            } else if (parts.length == 4) {
                // Intermediate node
                DecisionTreeNode node = new DecisionTreeNode(attribute, value, new ArrayList<>(), null);
                while (stack.size()-1>=depth){
                    stack.pop();
                }
                if (!stack.isEmpty()) {
                    DecisionTreeNode parent = stack.peek();
                    if (parent.branches == null) {
                        parent.branches = new ArrayList<>();
                    }
                    parent.branches.add(node);
                    stack.push(node);
                } else {
                    root.branches.add(node);
                    stack.push(node);
                }

            }
        }

        return root;
    }

    public static HashMap<String, List<String>> collectAttributesAndValues(DecisionTreeNode node, HashMap<String, List<String>> map) {
        if (node == null) {
            return map;
        }

        if (node.attribute != null && node.value != null) {
            if (!map.containsKey(node.attribute)) {
                map.put(node.attribute, new ArrayList<>());
            }
            if (!map.get(node.attribute).contains(node.value.substring(0, node.value.length() - 1))) {
                map.get(node.attribute).add(node.value.substring(0, node.value.length() - 1));
            }
        }

        if (node.branches != null) {
            for (DecisionTreeNode branch : node.branches) {
                collectAttributesAndValues(branch, map);
            }
        }

        return map;
    }

    public static void printTree(DecisionTreeNode node, int indent) {
        if (node == null) {
            return;
        }

        for (int i = 0; i < indent; i++) {
            System.out.print("  ");
        }

        if (node.attribute != null) {
            System.out.println(node.attribute + " = " + node.value);
        }

        if (node.result != null) {
            for (int i = 0; i < indent + 1; i++) {
                System.out.print("  ");
            }
            System.out.println("Result: " + node.result);
        }

        if (node.branches != null) {
            for (DecisionTreeNode branch : node.branches) {
                printTree(branch, indent + 1);
            }
        }
    }

    public static int getIndentLevel(String line) {
        int level = 0;
        int index = 0;

        while (index < line.length() && line.charAt(index) == '|') {
            level++;
            index++;

            // 跳过空格
            while (index < line.length() && line.charAt(index) == ' ') {
                index++;
            }
        }

        return level;
    }

}
